/**
 *
 * User: Zhikang.Peng
 * Date: 2018/05/30 10:22
 */
package references

import (
	"gitee.com/thghh/jvmgo/instructions/base"
	"gitee.com/thghh/jvmgo/rtda"
	"gitee.com/thghh/jvmgo/rtda/heap"
)

// 为指定的类的静态域赋值
type PUT_STATIC struct {
	base.Index16Instruction
}

/**
 为静态变量赋值
 1.先拿到当前方法、当前类和当前常量池，然后解析字段符号引用
 2.如果解析后的字段是实例字段而非静态字段，则抛出
IncompatibleClassChangeError异常。如果是final字段，则实际操作的
是静态常量，只能在类初始化方法中给它赋值。否则，会抛出
IllegalAccessError异常。类初始化方法由编译器生成，名字是
<clinit>
*/
func (self *PUT_STATIC) Execute(frame *rtda.Frame) {
	// 1
	currentMethod := frame.Method()
	currentClass := currentMethod.Class()
	cp := currentClass.ConstantPool()
	fieldRef := cp.GetConstant(self.Index).(*heap.FieldRef)
	field := fieldRef.ResolvedField()
	class := field.Class()
	if !class.InitStarted() {
		frame.RevertNextPC()
		base.InitClass(frame.Thread(), class)
		return
	}
	// 2
	if !field.IsStatic() {
		panic("java.lang.IncompatibleClassChangeError")
	}
	if field.IsFinal() {
		if currentClass != class || currentMethod.Name() != "<clinit>" {
			panic("java.lang.IllegalAccessError")
		}
	}
	// 3
	descriptor := field.Descriptor()
	slotId := field.SlotId()
	slots := class.StaticVars()
	stack := frame.OperandStack()
	switch descriptor[0] {
	case 'Z', 'B', 'C', 'S', 'I':
		slots.SetInt(slotId, stack.PopInt())
	case 'F':
		slots.SetFloat(slotId, stack.PopFloat())
	case 'J':
		slots.SetLong(slotId, stack.PopLong())
	case 'D':
		slots.SetDouble(slotId, stack.PopDouble())
	case 'L', '[':
		slots.SetReference(slotId, stack.PopReference())
	}
}
